package com.zbkj.crmeb.store.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.net.ssl.SSLContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.constants.Constants;
import com.constants.WeChatConstants;
import com.crmeb.core.exception.CrmebException;
import com.utils.PayUtil;
import com.utils.RestTemplateUtil;
import com.utils.WxPayUtil;
import com.zbkj.crmeb.payment.wechat.WeChatPayService;
import com.zbkj.crmeb.store.dao.StoreOrderDao;
import com.zbkj.crmeb.store.model.StoreOrder;
import com.zbkj.crmeb.store.request.StoreOrderRefundRequest;
import com.zbkj.crmeb.store.service.StoreOrderRefundService;
import com.zbkj.crmeb.system.service.SystemConfigService;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
* @author Mr.Zhang
* @Description StoreOrderServiceImpl 接口实现
* @since 2020-05-28
*/
@Service
public class StoreOrderRefundServiceImpl extends ServiceImpl<StoreOrderDao, StoreOrder> implements StoreOrderRefundService {

    private static Logger logger = LoggerFactory.getLogger(StoreOrderRefundServiceImpl.class);
    @Autowired
    WeChatPayService weChatPayService;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private RestTemplateUtil restTemplateUtil;

    private int socketTimeout = 10000;// 连接超时时间，默认10秒
    private int connectTimeout = 30000;// 传输超时时间，默认30秒
    private static RequestConfig requestConfig;// 请求器的配置
    private static CloseableHttpClient httpClient;// HTTP请求器

    /**
     * 退款 需要优化
     *
     * @author Mr.Zhang
     * @since 2020-06-03
     */
    @Override
    public Boolean refund(StoreOrderRefundRequest request, StoreOrder storeOrder) {
        try {
            //小程序
            if (storeOrder.getIsChannel() == Constants.ORDER_PAY_CHANNEL_PROGRAM) {
                //设置退款金额
                storeOrder.setRefundPrice(request.getAmount());
                return refund(storeOrder);
            }else{
                //weChatPayService.refund(storeOrder.getOrderId());
            }
        } catch (Exception e) {
            throw new CrmebException(e.getMessage());
        }
        return false;

    }


    /**
     * 退款接口
     * @return
     * @throws Exception
     */
    public Boolean refund(StoreOrder storeOrder) throws Exception {
        String appId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_ROUTINE_APP_ID + "_" + storeOrder.getMerId());
        appId = null == appId ? systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_ROUTINE_APP_ID): appId;
        String mchId = systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_ROUTINE_MCH_ID + "_" + storeOrder.getMerId());
        mchId = null == mchId ? systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_ROUTINE_MCH_ID) : mchId;
        String key = systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_WE_CHAT_APP_KEY + "_" + storeOrder.getMerId());
        key = null == key ? systemConfigService.getValueByKey(Constants.CONFIG_KEY_PAY_WE_CHAT_APP_KEY) : key;

        Map<String, Object> data = new HashMap<>();
        //退款到用户微信
        String nonce_str = getRandomStringByLength(32);
        data.put("appid", appId);
        data.put("mch_id", mchId);
        data.put("nonce_str", nonce_str);
        data.put("sign_type", "MD5");
        data.put("out_trade_no", storeOrder.getOrderId());//商户订单号
        data.put("out_refund_no", "tk" + storeOrder.getOrderId() + getRandomStringByLength(30));
        data.put("total_fee", String.valueOf(storeOrder.getPayPrice().multiply(new BigDecimal("100")).intValue()));//支付金额，微信支付提交的金额是不能带小数点的，且是以分为单位,这边需要转成字符串类型，否则后面的签名会失败
        data.put("refund_fee", String.valueOf(storeOrder.getRefundPrice().multiply(new BigDecimal("100")).intValue()));//退款总金额,订单总金额,单位为分,只能为整数
        String mySign = WxPayUtil.generateSignature(data, key, "MD5");
        data.put("sign", mySign);
        try {
            String xmlData = mapToXml(data);
            logger.error("微信退款入参：{}", xmlData);
            CloseableHttpResponse response = post(WeChatConstants.REFUND_PATH, xmlData, true, mchId, storeOrder.getMerId()+"");
            String responseXml = EntityUtils.toString(response.getEntity(), "utf-8");
            logger.error("微信退款返回结果：{}", responseXml);
            Map<String, String> responseMap = PayUtil.doXMLParse(responseXml);
            if ("SUCCESS".equals(responseMap.get("result_code"))) {
                logger.info("微信退款成功,订单号:{}", storeOrder.getOrderId());
                return true;
            } else {
                logger.info("订单={}失败原因:{}", storeOrder.getOrderId(), responseMap.get("err_code_des"));
                throw new CrmebException(responseMap.get("err_code_des"));
            }
        } catch (Exception e) {
            logger.error("微信退款异常:", e);
        }
        return true;
        /*String apiDomain = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_API_URL);
        //统一下单数据
        //data.put("notify_url",apiDomain + WeChatConstants.PAY_NOTIFY_API_URI_WECHAT);//退款成功后的回调地址
        //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String mySign = "";
        try {
            mySign = WxPayUtil.generateSignature(data, key, "MD5");
        } catch (Exception e) {
            e.printStackTrace();
        }
        data.put("sign", mySign);
        logger.info("请求微信退款参数:{}",data);
        //拼接统一下单接口使用的xml数据，要将上一步生成的签名一起拼接进去
        CloseableHttpResponse response = post(WeChatConstants.REFUND_PATH, mapToXml(data), true, mchId, storeOrder.getMerId()+"");
        String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8");
        logger.info("微信退款请求返回:{}",transfersXml);
        Map<String, Object> wxTradeResponse = PayUtil.doXMLParse(transfersXml);
        if("SUCCESS".equals(String.valueOf(wxTradeResponse.get("result_code")))){
            logger.info("微信退款成功,订单号:{}",storeOrder.getOrderId());
            return true;
        }else{
            logger.info("订单="+storeOrder.getOrderId()+"失败原因:{}" + wxTradeResponse.get("err_code_des"));
            throw new CrmebException(wxTradeResponse.get("err_code_des") + "");
        }*/
    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, Object> data) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.newDocument();
        Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key : data.keySet()) {
            String value = data.get(key).toString();
            if (value == null) {
                value = "";
            }
            value = value.trim();
            Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString();
        try {
            writer.close();
        } catch (Exception ex) {
        }
        return output;
    }

    /**
     * 指向你的证书的绝对路径，带着证书去访问
     */
    private static final String SSLCERT_PATH = "//data//cert//";
    private static final String SSLCERT_NAME = "apiclient_cert.p12";
    //private static final String SSLCERT_PATH = "D:\\refund\\apiclient_cert.p12";

    /**
     * 发送post请求
     *
     * @param url
     *            请求地址
     * @param outputEntity
     *            发送内容
     * @param isLoadCert
     *            是否加载证书
     */
    public static CloseableHttpResponse post(String url, String outputEntity, boolean isLoadCert, String key, String merId) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        httpPost.addHeader("Content-Type", "text/xml");
        // 添加缺失的 HTTP 头
//        httpPost.addHeader("Accept", "*/*");  // 可根据需求改为 application/json
//        httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36");

        httpPost.setEntity(new StringEntity(outputEntity, "UTF-8"));
        if (isLoadCert) {
            // 加载含有证书的http请求
            return HttpClients.custom().setSSLSocketFactory(initCert2(key, merId)).build().execute(httpPost);
        } else {
            return HttpClients.custom().build().execute(httpPost);
        }
    }

    /**
     * 加载证书
     */
    public static SSLConnectionSocketFactory initCert2(String key, String merId) throws Exception {
        FileInputStream instream = null;
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        instream = new FileInputStream(new File(SSLCERT_PATH + merId + "_" + SSLCERT_NAME));
        keyStore.load(instream, key.toCharArray());

        if (null != instream) {
            instream.close();
        }

        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

        return sslsf;
    }

    /**
     * 加载证书
     *
     */
    private static void initCert(String key) throws Exception {

        logger.info("开始加载证书---------------");
        // 商户证书的路径
        String path = "/data/cert/apiclient_cert.p12";
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 读取本机存放的PKCS12证书文件
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(new File(path));
            // 指定PKCS12的密码(商户ID)
            keyStore.load(fis, key.toCharArray());
        }catch (Exception e){
            logger.info("加载证书失败:{}",e);
        }finally {
            if(null!=fis) {
                fis.close();
            }
        }

//        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
//
//        // 指定TLS版本
//        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
//        // 设置httpclient的SSLSocketFactory
//        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
//        System.out.println(httpClient);

        logger.info("加载证书成功---------------");
    }


    /**
     * 通过Https往API post xml数据
     * @param url  API地址
     * @param xmlObj   要提交的XML数据对象
     * @return
     */
    public  String postData(String url, String xmlObj, String key) {
        // 加载证书
        try {
            initCert(key);
            //getCertificate("/data/cert/apiclient_cert.p12");
        } catch (Exception e) {
            logger.info("加载证书异常:{}",e);
        }
//        String xml = restTemplateUtil.postXml(url, xmlObj);
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);
        // 根据默认超时限制初始化requestConfig
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .build();
        // 设置请求器的配置
        httpPost.setConfig(requestConfig);
        try {
            HttpResponse response = null;
            try {
                CloseableHttpClient httpclient = HttpClients.createDefault();
                response = httpclient.execute(httpPost);
            }  catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            try {
                result = EntityUtils.toString(entity, "UTF-8");
            }  catch (IOException e) {
                e.printStackTrace();
            }
        } finally {
            httpPost.abort();
        }
        return result;
    }

    private  String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}


